package pl.edu.agh.nn.utils;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;

import pl.edu.agh.nn.conf.IConfigurable;
import pl.edu.agh.nn.conf.InvalidNetworkConfigurationException;
import pl.edu.agh.nn.net.INet;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.StaxDriver;

public class NetUtils implements IConfigurable {

	private String networkStructureDumpFile;
	private String learningErrorDumpFile;
	private BufferedWriter errorBufferedWriter;

	public void saveNetworkStructure(INet net) {
		if (networkStructureDumpFile != null) {
			StaxDriver driver = new StaxDriver();
			XStream xstream = new XStream(driver);
			xstream.aliasPackage("", "pl.edu.agh.nn");
			xstream.setMode(XStream.ID_REFERENCES);
			try {
				xstream.marshal(net,
						new PrettyPrintWriter(new BufferedWriter(new FileWriter(networkStructureDumpFile))));
			} catch (IOException e) {
				System.err.println("Could not marshal network");
				e.printStackTrace();
			}
		}
	}

	public void computeAndAddError(double[] expected, double[] received) {
		try {
			errorBufferedWriter.write("Expected: " + Arrays.toString(expected) + "\tError: "
					+ computeError(expected, received) + "\n");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void saveError() {
		if (errorBufferedWriter != null) {
			try {
				errorBufferedWriter.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void configure() throws InvalidNetworkConfigurationException {
		if (learningErrorDumpFile != null) {
			try {
				errorBufferedWriter = new BufferedWriter(new FileWriter(learningErrorDumpFile));
			} catch (Exception e) {
				System.err.println("Could not save data");
				e.printStackTrace();
			}
		}
	}

	private double computeError(double[] expected, double[] received) {
		int length = Math.min(expected.length, received.length);
		double error = 0.0;
		for (int i = 0; i < length; i++) {
			error += (expected[i] - received[i]) * (expected[i] - received[i]);
		}
		error = Math.pow(error / length, 1 / (double) length);
		return error;
	}

}
